#include <glib.h>

#define TEST_ARRAY_LEN 4

static void _test_hash_str_foreach_func(gpointer key, gpointer value, gpointer user_data)
{
    g_print("%s key: %s, value: %s \n", (gchar *)user_data, (gchar *)key, (gchar *)value);
    return;
}

static void _test_hash_str_key_destroy_func(gpointer data)
{
    if(NULL != data) {
        g_print("free key: %s \n", (gchar *)data);
        g_free(data);
    }
}

static void _test_hash_str_value_destroy_func(gpointer data)
{
    if(NULL != data) {
        g_print("free value: %s \n", (gchar *)data);
        g_free(data);
    }
}

static void test_hash_remove(void)
{
    gint i = 0;
    char *key[TEST_ARRAY_LEN] = {NULL};
    char *val[TEST_ARRAY_LEN] = {NULL};
    GHashTable *htable = NULL;

    g_print("\n");

    htable = g_hash_table_new_full(g_str_hash, g_str_equal, \
        _test_hash_str_key_destroy_func, _test_hash_str_value_destroy_func);

    for(i=0; i<TEST_ARRAY_LEN; i++) {
        key[i] = g_strdup_printf("key%d", i);
        val[i] = g_strdup_printf("value%d", i);
        g_hash_table_insert(htable, key[i], val[i]);
    }

    g_hash_table_foreach(htable, _test_hash_str_foreach_func, "ori");
    g_print("htable length: %d \n", g_hash_table_size(htable));

    g_hash_table_remove(htable, key[TEST_ARRAY_LEN/2]);

    g_hash_table_foreach(htable, _test_hash_str_foreach_func, "remove");
    g_print("htable length: %d \n", g_hash_table_size(htable));

    g_hash_table_destroy(htable);

    return;
}

static gboolean _test_hash_foreach_remove_ghrfunc
    (gpointer key, gpointer value, gpointer user_data)
{
    if((0 == g_strcmp0("key1", (const char *)key)) 
        || (0 == g_strcmp0("key3", (const char *)key)) ) {
        return TRUE;
    }

    return FALSE;
}

static void test_hash_foreach_remove(void)
{
    gint i = 0;
    char *key[TEST_ARRAY_LEN] = {NULL};
    char *val[TEST_ARRAY_LEN] = {NULL};
    GHashTable *htable = NULL;

    g_print("\n");

    htable = g_hash_table_new_full(g_str_hash, g_str_equal, \
        _test_hash_str_key_destroy_func, _test_hash_str_value_destroy_func);

    for(i=0; i<TEST_ARRAY_LEN; i++) {
        key[i] = g_strdup_printf("key%d", i);
        val[i] = g_strdup_printf("value%d", i);
        g_hash_table_insert(htable, key[i], val[i]);
    }

    g_hash_table_foreach(htable, _test_hash_str_foreach_func, "ori");
    g_print("htable length: %d \n", g_hash_table_size(htable));

    g_hash_table_foreach_remove(htable, _test_hash_foreach_remove_ghrfunc, NULL);

    g_hash_table_foreach(htable, _test_hash_str_foreach_func, "foreach_remove");
    g_print("htable length: %d \n", g_hash_table_size(htable));

    g_hash_table_destroy(htable);

    return;
}

static void test_hash_remove_all(void)
{
    gint i = 0;
    char *key[TEST_ARRAY_LEN] = {NULL};
    char *val[TEST_ARRAY_LEN] = {NULL};
    GHashTable *htable = NULL;

    g_print("\n");

    htable = g_hash_table_new_full(g_str_hash, g_str_equal, \
        _test_hash_str_key_destroy_func, _test_hash_str_value_destroy_func);

    for(i=0; i<TEST_ARRAY_LEN; i++) {
        key[i] = g_strdup_printf("key%d", i);
        val[i] = g_strdup_printf("value%d", i);
        g_hash_table_insert(htable, key[i], val[i]);
    }

    g_hash_table_foreach(htable, _test_hash_str_foreach_func, "ori");
    g_print("htable length: %d \n", g_hash_table_size(htable));

    g_hash_table_remove_all(htable);

    g_hash_table_foreach(htable, _test_hash_str_foreach_func, "remove_all");
    g_print("htable length: %d \n", g_hash_table_size(htable));

    g_hash_table_destroy(htable);

    return;
}

static void test_hash_steal(void)
{
    gint i = 0;
    gboolean ret = FALSE;
    char *key[TEST_ARRAY_LEN] = {NULL};
    char *val[TEST_ARRAY_LEN] = {NULL};
    GHashTable *htable = NULL;

    g_print("\n");

    htable = g_hash_table_new_full(g_str_hash, g_str_equal, \
        _test_hash_str_key_destroy_func, _test_hash_str_value_destroy_func);

    for(i=0; i<TEST_ARRAY_LEN; i++) {
        key[i] = g_strdup_printf("key%d", i);
        val[i] = g_strdup_printf("value%d", i);
        g_hash_table_insert(htable, key[i], val[i]);
    }

    g_hash_table_foreach(htable, _test_hash_str_foreach_func, "ori");
    g_print("htable length: %d \n", g_hash_table_size(htable));

    ret = g_hash_table_steal(htable, key[TEST_ARRAY_LEN/2]);
    if(TRUE == ret) {
        g_print("found key: %s \n", key[TEST_ARRAY_LEN/2]);
        g_free(key[TEST_ARRAY_LEN/2]);
        g_free(val[TEST_ARRAY_LEN/2]);
    }

    g_hash_table_foreach(htable, _test_hash_str_foreach_func, "steal");
    g_print("htable length: %d \n", g_hash_table_size(htable));

    g_hash_table_destroy(htable);

    return;
}

static gboolean _test_hash_foreach_steal_ghrfunc
    (gpointer key, gpointer value, gpointer user_data)
{
    if((0 == g_strcmp0("key1", (const char *)key)) 
        || (0 == g_strcmp0("key3", (const char *)key)) ) {
        return TRUE;
    }

    return FALSE;
}

static void test_hash_foreach_steal(void)
{
    gint i = 0;
    gboolean ret = FALSE;
    char *key[TEST_ARRAY_LEN] = {NULL};
    char *val[TEST_ARRAY_LEN] = {NULL};
    GHashTable *htable = NULL;

    g_print("\n");

    htable = g_hash_table_new_full(g_str_hash, g_str_equal, \
        _test_hash_str_key_destroy_func, _test_hash_str_value_destroy_func);

    for(i=0; i<TEST_ARRAY_LEN; i++) {
        key[i] = g_strdup_printf("key%d", i);
        val[i] = g_strdup_printf("value%d", i);
        g_hash_table_insert(htable, key[i], val[i]);
    }

    g_hash_table_foreach(htable, _test_hash_str_foreach_func, "ori");
    g_print("htable length: %d \n", g_hash_table_size(htable));

    ret = g_hash_table_foreach_steal(htable, _test_hash_foreach_steal_ghrfunc, NULL);
    if(2 == ret) {
        g_print("steal key1 and key3 \n");
        g_free(key[1]);
        g_free(val[1]);
        g_free(key[3]);
        g_free(val[3]);
    }

    g_hash_table_foreach(htable, _test_hash_str_foreach_func, "foreach_steal");
    g_print("htable length: %d \n", g_hash_table_size(htable));

    g_hash_table_destroy(htable);

    return;
}

static void test_hash_steal_all(void)
{
    gint i = 0;
    char *key[TEST_ARRAY_LEN] = {NULL};
    char *val[TEST_ARRAY_LEN] = {NULL};
    GHashTable *htable = NULL;

    g_print("\n");

    htable = g_hash_table_new_full(g_str_hash, g_str_equal, \
        _test_hash_str_key_destroy_func, _test_hash_str_value_destroy_func);

    for(i=0; i<TEST_ARRAY_LEN; i++) {
        key[i] = g_strdup_printf("key%d", i);
        val[i] = g_strdup_printf("value%d", i);
        g_hash_table_insert(htable, key[i], val[i]);
    }

    g_hash_table_foreach(htable, _test_hash_str_foreach_func, "ori");
    g_print("htable length: %d \n", g_hash_table_size(htable));

    g_hash_table_steal_all(htable);
    for(i=0; i<TEST_ARRAY_LEN; i++) {
        g_free(key[i]);
        g_free(val[i]);
    }

    g_hash_table_foreach(htable, _test_hash_str_foreach_func, "steal_all");
    g_print("htable length: %d \n", g_hash_table_size(htable));

    g_hash_table_destroy(htable);

    return;
}

gint main(gint argc, gchar **argv)
{
    g_test_init(&argc, &argv, NULL);

    g_test_add_func ("/hash/remove", test_hash_remove);
    g_test_add_func ("/hash/foreach-remove", test_hash_foreach_remove);
    g_test_add_func ("/hash/remove-all", test_hash_remove_all);
    g_test_add_func ("/hash/steal", test_hash_steal);
    g_test_add_func ("/hash/foreach-steal", test_hash_foreach_steal);
    g_test_add_func ("/hash/steal-all", test_hash_steal_all);

    return g_test_run();
}